package Model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.ListIterator;

import Messaging.Notification;
import Messaging.NotificationManager;
import Ui.ActionMessage;
import Ui.PropertySelectionSellPanel;

/*
 * @author Roch San Buenaventura
 */

public class Player implements Serializable 
{

	final static long serialVersionUID = 209;
	
	private final int SPACES_ON_BOARD = 40;
	private PlayerState State_;
	private String Name_;
	private int Cash_ = 1500, PositionOnBoard_, Index_;
	private boolean getOutOfJail_, sellPanelVisible_;
	private ArrayList<PropertySpace> Properties_;
	
	private int numOfRolls_ = 0;

	protected Player() 
	{
		State_ = PlayerFree.Instance();
		Name_ = "ANONYMOUS";
		Cash_ = 1500;
		Index_ = 0;
		PositionOnBoard_ = 0;
		getOutOfJail_ = false;
		Properties_ = new ArrayList<PropertySpace>();
		numOfRolls_ = 0;
		sellPanelVisible_ = false;
	}

	public Player(int index, String name) 
	{
		State_ = PlayerFree.Instance();
		Name_ = name;
		Cash_ = 1500;
		Index_ = index;
		PositionOnBoard_ = 0;
		getOutOfJail_ = false;
		Properties_ = new ArrayList<PropertySpace>();
		numOfRolls_ = 0;
		sellPanelVisible_ = false;
	}
	
	public Player(Player player) 
	{
		State_ = player.State_;
		Name_ = player.getName();
		Cash_ = player.getCash();
		Index_ = player.getIndex();
		PositionOnBoard_ = player.getPosition();
		getOutOfJail_ = player.hasGetOutOfJail();
		Properties_ = new ArrayList<PropertySpace>(player.getProperties());
		numOfRolls_ = player.getNumOfRolls();
		sellPanelVisible_ = false;
	}

	public void changeState(PlayerState newState) 
	{
		State_ = newState;
	}

	public PlayerState getState() 
	{
		return State_;
	}

	public void movePiece(int numOfSpaces) 
	{
		State_.movePiece(this, numOfSpaces);
	}
	
	public void movePiece(Dice dice) 
	{
		State_.movePiece(this, dice);
	}

	public void collectRent(int amount, Player payer) 
	{
		State_.collectRent(this, amount, payer);
	}

	public void goToJail() 
	{
		NotificationManager.getInstance().notifyObservers(Notification.END_TURN_EARLY, null);
		setPosition(10);
		State_.goToJail(this);
	}

	public void getOutOfJail() 
	{
		State_.getOutOfJail(this);
	}

	public void updatePosition(int numOfSpaces) 
	{
		NotificationManager.getInstance().notifyObservers(Notification.PIECE_MOVE_SPACES, numOfSpaces);

		if (((PositionOnBoard_ + numOfSpaces) != 40) && (PositionOnBoard_ + numOfSpaces) >= SPACES_ON_BOARD) 
		{
			updateCash(200);
			NotificationManager.getInstance().notifyObservers(Notification.ACTION_MESSAGE, getName() + " has been awarded $200 for passing GO!");
		}
		
		PositionOnBoard_ = (PositionOnBoard_ + numOfSpaces) % SPACES_ON_BOARD;
	}

	public void setPosition(int space) 
	{
		if (space >= 0 && space < SPACES_ON_BOARD) 
		{
			PositionOnBoard_ = space;
			NotificationManager.getInstance().notifyObservers(Notification.PIECE_MOVE_TO, space);
		}
		
		else
			System.err.println("Invalid space number for setPosition: " + space);
	}
 
	public void setPosition_NoNotify(int space) 
	{
		if (space >= 0 && space < SPACES_ON_BOARD) 
			PositionOnBoard_ = space;
		else
			System.err.println("Invalid space number for setPosition: " + space);
	}

	public int getPosition() 
	{
		return PositionOnBoard_;
	}

	public void updateCash(int value) 
	{
		Cash_ += value;
		int propertyValue = 0;
		if(Cash_ < 0) {
			for(int x=0; x < Properties_.size(); x++) {
				if(!Properties_.get(x).getState().toString().equals(SpaceMortgaged.Instance().toString())) propertyValue += Properties_.get(x).getMortgageValue();
				
				if(Properties_.get(x).isRenovated())
				{
					if(Properties_.get(x).getState().getLevel() == 5)
						
						propertyValue += 200;
					
					else
						propertyValue += Properties_.get(x).getState().getLevel() * 25;
				}
			}

			if(Cash_ + propertyValue > 0) 
			{
				ActionMessage.getInstance().newMessage("You Lost!");
				for(int x=0; x < Properties_.size(); x++) 
				{
					Properties_.get(x).changeState(SpaceUnowned.Instance());
				}
				
				NotificationManager.getInstance().notifyObservers(Notification.REMOVE_PLAYER, null);
			} 
			
		}
		
		Player tempPlayer = new Player(this);
		NotificationManager.getInstance().notifyObservers(Notification.UPDATE_CASH, tempPlayer);
	}

	public void setCash(int value) 
	{
		Cash_ = value;
	}

	public int getCash() 
	{
		return Cash_;
	}

	public void setGetOutOfJail(boolean hasCard) 
	{
		getOutOfJail_ = hasCard;		
	}
	
	public void usedGetOutOfJail() 
	{
		getOutOfJail_ = false;
		NotificationManager.getInstance().notifyObservers(Notification.USED_JAIL_CARD, new Player(this));
	}
	
	public void gainedGetOutOfJail() 
	{
		getOutOfJail_ = true;
		NotificationManager.getInstance().notifyObservers(Notification.GAINED_JAIL_CARD, new Player(this));	
	}

	public boolean hasGetOutOfJail() 
	{
		return getOutOfJail_;
	}

	public void setName(String name) 
	{
		Name_ = name;
	}

	public String getName() 
	{
		return Name_;
	}

	public void setProperties(ArrayList<PropertySpace> properties) 
	{
		Properties_ = properties;
	}

	public ArrayList<PropertySpace> getProperties() 
	{
		return Properties_;
	}
	
	public String[] getPropertyArray() 
	{
		String [] array = new String[Properties_.size()];
		
		for (int i = 0; i < Properties_.size(); i++)
			array[i] = Properties_.get(i).getNameAndStatus();
		
		return array;
	}
	
	public int getIndex() 
	{
		return Index_;
	}
	
	public void setIndex(int i) 
	{
		Index_ = i;
	}
	
	public void setNumOfRolls(int i) 
	{
		numOfRolls_ = i;
	}
	
	public int getNumOfRolls() 
	{
		return numOfRolls_;
	}
	
	public void purchase(PropertySpace property) 
	{
		if (property.getPurchasePrice() < Cash_) 
		{
			updateCash(-1 * property.getPurchasePrice());
			property.bePurchased(this);
			updateProperties(property);
			
			NotificationManager.getInstance().notifyObservers(Notification.ACTION_MESSAGE, Name_ + " purchased " + property.getName());

			NotificationManager.getInstance().notifyObservers(Notification.DISABLE_PURCHASE, null);
		}
	}
	
	public void updateProperties(PropertySpace property) 
	{
		if (Properties_.size() == 0)
			Properties_.add(property);
		
		else 
		{
			ListIterator<PropertySpace> itr = Properties_.listIterator(Properties_.size());
			PropertySpace tempSpace = null;
			
			while (itr.hasPrevious()) 
			{
				tempSpace = itr.previous();
				
				if (property.getTypeInt() == tempSpace.getTypeInt()) 
				{
					if (property.getSpaceNumber() < tempSpace.getSpaceNumber())
						Properties_.add(itr.nextIndex(), property);
					
					else
						Properties_.add(1 + itr.nextIndex(), property);
					
					break;
				}
				
				else if (property.getTypeInt() > tempSpace.getTypeInt()) 
				{
					Properties_.add(1 + (itr.nextIndex()), property);
					break;
				}
			}
			
			if (property.getTypeInt() < tempSpace.getTypeInt())
				Properties_.add(itr.nextIndex(), property);
		}
		
		Player tempPlayer = new Player(this);
		NotificationManager.getInstance().notifyObservers
			(Notification.UPDATE_PROPERTIES, tempPlayer);
	}
	
	public void updateProperties_withoutNotification(PropertySpace property) 
	{
		if (Properties_.size() == 0)
			Properties_.add(property);
		
		else 
		{
			ListIterator<PropertySpace> itr = Properties_.listIterator(Properties_.size());
			PropertySpace tempSpace = null;
			
			while (itr.hasPrevious()) 
			{
				tempSpace = itr.previous(); 
				if (property.getTypeInt() == tempSpace.getTypeInt()) 
				{
					if (property.getSpaceNumber() < tempSpace.getSpaceNumber())
						Properties_.add(itr.nextIndex(), property);
					
					else
						Properties_.add(1 + itr.nextIndex(), property);
					
					break;
				}
				
				else if (property.getTypeInt() > tempSpace.getTypeInt()) 
				{
					Properties_.add(1 + (itr.nextIndex()), property);
					break;
				}
			}
			
			if (property.getTypeInt() < tempSpace.getTypeInt())
				Properties_.add(itr.nextIndex(), property);
		}
	}
	
	public boolean renovateProperty(UpgradeablePropertySpace p) 
	{
		if (Cash_ >= 50 && p.getState().getLevel() < 4) 
		{
			updateCash(-50);
			p.renovate();
			return true;
		}
		
		else if (Cash_ >= 200 && p.getState().getLevel() == 4) {
			updateCash(-200);
			p.renovate();
			return true;
		}
		
		return false;
	}
	
	public void downgradeProperty(UpgradeablePropertySpace p) 
	{
		if (p.getState().getLevel() < 5) 
		{
			updateCash(25);
			p.downgrade();
		}
		
		else 
		{
			updateCash(100);
			p.downgrade();
		}
	}

	public void mortgage(PropertySpace property) 
	{
		updateCash(property.getMortgageValue());
		property.beMortgaged();
		NotificationManager.getInstance().notifyObservers
		(Notification.UPDATE_PROPERTIES, new Player(this));
	}

	public void unmortgage(PropertySpace property) 
	{
		if (getCash() - property.getMortgageValue() > 0) 
		{
			updateCash(-property.getMortgageValue());
			property.unmortgage();
			NotificationManager.getInstance().notifyObservers
			(Notification.UPDATE_PROPERTIES, new Player(this));
		}
		
		else
			System.out.println("Can't Unmortgage "+getName()+", not enough cash");
	}
	
	public SpaceState updateTypeIncrease(int type_) 
	{
		SpaceState newState = null;
		
		for (int i = 0; i < Properties_.size(); i++) 
		{
			if (type_ == Properties_.get(i).getTypeInt() && !Properties_.get(i).getState().toString().equals(SpaceMortgaged.Instance().toString())) {
				Properties_.get(i).ownershipIncrease();
				newState = Properties_.get(i).getState();
			}
		}
		
		return newState;
	}
	
	public SpaceState updateTypeDecrease(int type_) 
	{
		SpaceState newState = null;
		
		for (int i = 0; i < Properties_.size(); i++) 
		{
			if (type_ == Properties_.get(i).getTypeInt() && !Properties_.get(i).getState().toString().equals(SpaceMortgaged.Instance().toString())) {
				Properties_.get(i).ownershipDecrease();
				newState = Properties_.get(i).getState();
			}
		}
		
		return newState;
	}
	
	public int countProperties(int type) 
	{
		int counter = 0;
		
		for (int i = 0; i < Properties_.size(); i++)
		{
			if (Properties_.get(i).getTypeInt() == type)
				counter++;
		}
		
		return counter;
	}
	
	public boolean hasMonopoly(int type) 
	{
		return (countProperties(type) == PropertySpace.propertiesForMonopoly_[type]);
	}
	
	public boolean monopolyUpgradeValid(UpgradeablePropertySpace p) 
	{
		int propertyCounter = 0;
		
		for (int i = 0; i < Properties_.size(); i++) 
		{
			if (Properties_.get(i).getTypeInt() == p.getTypeInt()) 
			{
				propertyCounter++;
				
				if (Properties_.get(i).getState().getLevel() < p.getState().getLevel() || Properties_.get(i).getState().toString().equals(SpaceMortgaged.Instance().toString())) {
					
					System.err.println(Properties_.get(i).getState().getLevel());
					System.err.println(Properties_.get(i).getNameAndStatus());
					return false;
				}
			}
		}
		
		if (propertyCounter == PropertySpace.propertiesForMonopoly_[p.getTypeInt()])
			return true;
		
		return false;
	}
	
	public boolean monopolyDowngradeValid(UpgradeablePropertySpace p) 
	{
		for (int i = 0; i < Properties_.size(); i++) 
		{
			if (Properties_.get(i).getTypeInt() == p.getTypeInt()) 
			{
				
				if (Properties_.get(i).getState().getLevel() > p.getState().getLevel())
					return false;
			}
		}
		
		return true;
	}

	public boolean propertiesRenovated(int type) 
	{
		for (int i = 0; i < Properties_.size(); i++) 
		{
			if (Properties_.get(i).getTypeInt() == type && Properties_.get(i).getState().getLevel() != 0) {
				return true;
			}
		}
		
		return false;
	}
}
